﻿using System;
using System.Collections.Generic;
using System.Globalization;
using System.Security.Cryptography;
using System.Text;
using System.Web.Mvc;
using Nop.Core;
using Nop.Core.Domain.Orders;
using Nop.Core.Domain.Payments;
using Nop.Plugin.Payments.WalletOne.Domain;
using Nop.Plugin.Payments.WalletOne.Models;
using Nop.Services.Configuration;
using Nop.Services.Localization;
using Nop.Services.Logging;
using Nop.Services.Orders;
using Nop.Services.Payments;
using Nop.Web.Framework;
using Nop.Web.Framework.Controllers;

namespace Nop.Plugin.Payments.WalletOne.Controllers
{
    public class PaymentWalletOneController : BaseNopPaymentController
    {
        private readonly ISettingService _settingService;
		private readonly ILocalizationService _localizationService;
		private readonly IPaymentService _paymentService;
        private readonly IOrderService _orderService;
        private readonly IOrderProcessingService _orderProcessingService;
		private readonly ILogger _logger;
		private readonly IWebHelper _webHelper;
        private readonly WalletOnePaymentSettings _walletOnePaymentSettings;
        private readonly PaymentSettings _paymentSettings;
        private readonly IWorkContext _workContext;

		public PaymentWalletOneController(ISettingService settingService,
			ILocalizationService localizationService, 
            IPaymentService paymentService, IOrderService orderService,
			IOrderProcessingService orderProcessingService,
			ILogger logger, IWebHelper webHelper,
			WalletOnePaymentSettings walletOnePaymentSettings,
            PaymentSettings paymentSettings, IWorkContext workContext)
        {
            this._settingService = settingService;
			this._localizationService = localizationService;
			this._paymentService = paymentService;
            this._orderService = orderService;
            this._orderProcessingService = orderProcessingService;
			this._logger = logger;
			this._webHelper = webHelper;
			this._walletOnePaymentSettings = walletOnePaymentSettings;
            this._paymentSettings = paymentSettings;
            this._workContext = workContext;
        }
        
        [AdminAuthorize]
        [ChildActionOnly]
        public ActionResult Configure()
        {
            var model = new ConfigurationModel();
			model.MerchantId = _walletOnePaymentSettings.MerchantId;
			model.SecretKey = _walletOnePaymentSettings.SecretKey;
			model.AdditionalFee = _walletOnePaymentSettings.AdditionalFee;

			var services = new WalletOneServices();
			// Load service names
			string paymentMethodsOfferedDomestic = _walletOnePaymentSettings.PaymentMethodsOffered;
			foreach (string service in services.Services)
				model.AvailablePaymentMethods.Add(service);

			if (!String.IsNullOrEmpty(paymentMethodsOfferedDomestic))
				foreach (string service in services.Services)
				{
					string serviceId = WalletOneServices.GetServiceId(service);
					if (!String.IsNullOrEmpty(serviceId) && !String.IsNullOrEmpty(paymentMethodsOfferedDomestic))
					{
						if (paymentMethodsOfferedDomestic.Contains(serviceId))
							model.PaymentMethodsOffered.Add(service);
					}
				}

			return View("Nop.Plugin.Payments.WalletOne.Views.PaymentWalletOne.Configure", model);
        }

        [HttpPost]
        [AdminAuthorize]
        [ChildActionOnly]
        public ActionResult Configure(ConfigurationModel model)
        {
            if (!ModelState.IsValid)
                return Configure();

            //save settings
			_walletOnePaymentSettings.MerchantId = model.MerchantId;
			_walletOnePaymentSettings.SecretKey = model.SecretKey;
			_walletOnePaymentSettings.AdditionalFee = model.AdditionalFee;

			// Save selected services
			var paymentMethodsOfferedDomestic = new StringBuilder();
			int paymentMethodsDomesticSelectedCount = 0;
			if (model.CheckedPaymentMethods != null)
			{
				foreach (var cs in model.CheckedPaymentMethods)
				{
					paymentMethodsDomesticSelectedCount++;
					string serviceId = WalletOneServices.GetServiceId(cs);
					if (!String.IsNullOrEmpty(serviceId))
						paymentMethodsOfferedDomestic.AppendFormat("{0}:", serviceId);
				}
			}
			// Add default options if no services were selected
			if (paymentMethodsDomesticSelectedCount == 0)
				_walletOnePaymentSettings.PaymentMethodsOffered = "WalletOneRUB:YandexMoneyRUB:RbkMoneyRUB:SvyaznoyBankRUB:BankTransferRUB:LiqPayRUB";
			else
				_walletOnePaymentSettings.PaymentMethodsOffered = paymentMethodsOfferedDomestic.ToString().TrimEnd(new char[] { ':' });

			_settingService.SaveSetting(_walletOnePaymentSettings);

			return Configure();
        }

        [ChildActionOnly]
        public ActionResult PaymentInfo()
        {
            var model = new PaymentInfoModel();
			return View("Nop.Plugin.Payments.WalletOne.Views.PaymentWalletOne.PaymentInfo", model);
        }

        [NonAction]
        public override IList<string> ValidatePaymentForm(FormCollection form)
        {
            var warnings = new List<string>();
            return warnings;
        }

        [NonAction]
        public override ProcessPaymentRequest GetPaymentInfo(FormCollection form)
        {
            var paymentInfo = new ProcessPaymentRequest();
            return paymentInfo;
        }

		private ActionResult Answer(string result, string description)
		{
			return new ContentResult() { Content = string.Format("WMI_RESULT={0}&WMI_DESCRIPTION={1}", result, description), ContentType = "text/plain" };
		}

        [ValidateInput(false)]
        public ActionResult Return(FormCollection form)
        {
			var processor = _paymentService.LoadPaymentMethodBySystemName("Payments.WalletOne") as WalletOnePaymentProcessor;
            if (processor == null ||
                !processor.IsPaymentMethodActive(_paymentSettings) || !processor.PluginDescriptor.Installed)
				throw new NopException("WalletOne module cannot be loaded");

			int orderId = 0;
			if (!int.TryParse(form["WMI_PAYMENT_NO"], out orderId))
				return Answer("Retry", string.Format("Неверный номер заказа: {0}", orderId));

			string orderState = form["WMI_ORDER_STATE"];
			if (orderState != "Accepted")
				return Answer("Retry", string.Format("Неверное состояние оплаты заказа #{0} - {1}", orderId, orderState));

			string signature = form["WMI_SIGNATURE"];
			SortedDictionary<string, string> sortedParams = new SortedDictionary<string, string>();
			foreach (string key in form.AllKeys)
				sortedParams.Add(key, form[key]);

			StringBuilder signatureData = new StringBuilder();
			foreach (string key in sortedParams.Keys)
				if (key != "WMI_SIGNATURE") signatureData.Append(sortedParams[key]);

			Byte[] bytes = Encoding.GetEncoding(1251).GetBytes(signatureData.ToString() + _walletOnePaymentSettings.SecretKey);
			Byte[] hash = new MD5CryptoServiceProvider().ComputeHash(bytes);

			if (signature != Convert.ToBase64String(hash))
				return Answer("Retry", string.Format("Неверная подпись: {0}", signature));

			Order order = _orderService.GetOrderById(orderId);

			if (order == null || order.Deleted)
				return new HttpUnauthorizedResult();

			string amount = form["WMI_PAYMENT_AMOUNT"];
			string ordercurrency = form["WMI_CURRENCY_ID"];
			string description = form["WMI_DESCRIPTION"];
			string expired = form["WMI_EXPIRED_DATE"];
			string walletId = form["WMI_TO_USER_ID"];

			var sb = new StringBuilder();
			sb.AppendLine("WalletOne payment result:");
			sb.AppendLine("Order state: " + orderState);
			sb.AppendLine("Amount: " + amount);
			sb.AppendLine("Currency: " + ordercurrency);
			sb.AppendLine("Expired date: " + expired);
			sb.AppendLine("User wallet: " + walletId);

			//order note
			order.OrderNotes.Add(new OrderNote()
			{
				Note = sb.ToString(),
				DisplayToCustomer = false,
				CreatedOnUtc = DateTime.UtcNow
			});
			_orderService.UpdateOrder(order);

			//mark order as paid
			order.AuthorizationTransactionResult = orderState;
			if (_orderProcessingService.CanMarkOrderAsPaid(order))
				_orderProcessingService.MarkOrderAsPaid(order);

			return Answer("Ok", string.Format("Заказ #{0} оплачен", order.Id));
		}
    }
}